package uz.rt.dicom.data.element;

import uz.rt.dicom.Padding;
import uz.rt.dicom.ValueRepresentation;
import uz.rt.dicom.data.ComparableList;
import uz.rt.dicom.data.StringElement;
import uz.rt.dicom.io.DicomIOException;
import uz.rt.dicom.io.DicomInputStream;
import uz.rt.dicom.tag.DicomTag;

import java.io.IOException;

public class DateTime extends StringElement
{
// ------------------------------ FIELDS ------------------------------

    /** Date and time format */
    private String format = "YYYYMMDDHHMMSS.FFFFFF&ZZXX";

// --------------------------- CONSTRUCTORS ---------------------------

    public DateTime(DicomTag tag)
    {
        super(tag);
        setMaxLength(26);
        setPadding(Padding.TRAILING_SPACE);
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface IDataElement ---------------------

    @Override
    public byte[] getVR()
    {
        return ValueRepresentation.DA;
    }

    @Override
    public ValueRepresentation getValueRepresentation()
    {
        return ValueRepresentation.Date;
    }

    public void read(DicomInputStream dicomInputStream, int length) throws IOException
    {
        value = null;
        value = new ComparableList<String>();
        String s = dicomInputStream.readString(length);
        String[] groups = splitValues(s);
        if (groups.length < vm.getMin() || groups.length > vm.getMax())
        {
            value = null;
            throw new DicomIOException(
                    "Value multiplicity violation: group length = " + groups.length +
                            ", limits are [" + vm.toString() + "]");
        }
        char c;
        for (String group : groups)
        {
            for (int i = 0; i < group.length(); i++)
            {
                c = group.charAt(i);
                if (!Character.isDigit(c) && c != '+' && c != '-' && c != '.')
                {
                    value = null;
                    throw new DicomIOException(
                            "Character \"" + c + " \" is not allow. Allowed characters are: [0-9], '+', '-', '.', ' '");
                }
            }
            value.add(group);
        }
    }

// -------------------------- OTHER METHODS --------------------------

    /**
     * The function compares only the following parameters in order:
     * <ul>
     * <li>Dicom tag</li>
     * <li>Value multiplicity</li>
     * <li>Comparable list of data fields</li>
     * </ul>
     *
     * @param o A DateTime element containing a value of the ComparableList datatype
     * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
     *         the specified object.
     */
    public int compareTo(DateTime o)
    {
        if (!tag.equals(o.getTag())) return tag.compareTo(o.getTag());
        if (!vm.equals(o.getVm())) return vm.compareTo(o.getVm());
        return value.compareTo(o.getValue());
    }

    /**
     * Get the day based on the internal value.
     * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
     *
     * @param index Index of the value that needs to be accessed. Value should be with the constraints of the value
     *              multiplicity.
     * @return Day
     */
    public int getDay(int index)
    {
        if (index >= 0 && index < value.size())
        {
            if (value.get(index).length() >= 4) return Integer.valueOf(value.get(index).substring(6, 8));
        }
        return Integer.MIN_VALUE;
    }

    /**
     * Get the number of hours.
     * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
     *
     * @param index Index of the value that needs to be accessed. Value should be with the constraints of the value
     *              multiplicity.
     * @return Hour
     */
    public int getHour(int index)
    {
        if (index >= 0 && index < value.size())
        {
            if (value.get(index).length() >= 10) return Integer.valueOf(value.get(index).substring(8, 10));
        }
        return Integer.MIN_VALUE;
    }

    /**
     * Get the number of minutes.
     * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
     *
     * @param index Index of the value that needs to be accessed. Value should be with the constraints of the value
     *              multiplicity.
     * @return Hour
     */
    public int getMinutes(int index)
    {
        if (index >= 0 && index < value.size())
        {
            if (value.get(index).length() >= 12) return Integer.valueOf(value.get(index).substring(10, 12));
        }
        return Integer.MIN_VALUE;
    }

    /**
     * Get the month based on the internal value.
     * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
     *
     * @param index Index of the value that needs to be accessed. Value should be with the constraints of the value
     *              multiplicity.
     * @return Month
     */
    public int getMonth(int index)
    {
        if (index >= 0 && index < value.size())
        {
            if (value.get(index).length() >= 4) return Integer.valueOf(value.get(index).substring(4, 6));
        }
        return Integer.MIN_VALUE;
    }

    /**
     * Get the number of seconds.
     * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
     *
     * @param index Index of the value that needs to be accessed. Value should be with the constraints of the value
     *              multiplicity.
     * @return Seconds
     */
    public int getSeconds(int index)
    {
        if (index >= 0 && index < value.size())
        {
            if (value.get(index).length() >= 14) return Integer.valueOf(value.get(index).substring(12, 14));
        }
        return Integer.MIN_VALUE;
    }

    /**
     * Get the UTC offset (ex: +500 or -1100)
     *
     * @param index Index of the value that needs to be accessed. Value should be with the constraints of the value
     *              multiplicity.
     * @return UTC offset
     * @throws uz.rt.dicom.io.DicomIOException
     *          If an IO error occurs.
     */
    public int getUtcOffset(int index) throws DicomIOException
    {
        throw new DicomIOException("Not yet implemented");
    }

    /**
     * Get the year based on the internal value.
     * If the has not been parsed properly, its value will be set at Integer.MIN_VALUE
     *
     * @param index Index of the value that needs to be accessed. Value should be with the constraints of the value
     *              multiplicity.
     * @return Year
     */
    public int getYear(int index)
    {
        if (index >= 0 && index < value.size())
        {
            if (value.get(index).length() >= 4) return Integer.valueOf(value.get(index).substring(0, 4));
        }
        return Integer.MIN_VALUE;
    }

    /**
     * Set the UTC offset (ex: +500 or -1100)
     *
     * @param index     Index of the value that needs to be accessed. Value should be with the constraints of the value
     *                  multiplicity.
     * @param utcOffset UTC offset
     * @throws uz.rt.dicom.io.DicomIOException
     *          If an IO error occurs.
     */
    public void setUtcOffset(int index, int utcOffset) throws DicomIOException
    {
        throw new DicomIOException("Not yet implemented");
    }
}
